#ifndef _SELECT_H_2016_12_02_
#define _SELECT_H_2016_12_02_
#include "net.h"
//only gw server && transmit server use 
#include "../platform/_platform_def.h"

#ifndef PLATFORM_WIN32
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#else
// #include <WinSock2.h>
static int s_count ;
#endif 
#include <math.h>
#include <iostream>
#include "../event/event.pb.h"
class netselect:public inetselctor
{
public:
	netselect(router* rtr,io_handler* parent,std::string ip,unsigned int port):_router(rtr),_io_hander(parent),_status(NS_WAIT),_exit(false),_fd(-1),_ip(ip),_port(port)
	{
		s_count = 0;
	}
	virtual ~netselect()
	{
		_status =NS_EXIT;
		_exit = true;
	}

public:
	virtual int start()
	{
		if(_router->routertype() == RT_GW )
		{
			initsock();
			connecttransmitsvr(925,"127.0.0.1");
		}
		else if( _router->routertype() == RT_TRANSMIT)
		{
			initsock();
		}
		else
		{
			//other 
			connecttransmitsvr(925,"127.0.0.1");
		}
			
		_status = NS_RUN;
		int max_fd=0;
		FD_ZERO(&_fdset);
		FD_SET(_fd,&_fdset);
		max_fd = _fd;
		fd_set fdset;
		do 
		{
			timeval tv;
			tv.tv_sec =1;
			tv.tv_usec =0;
			fdset = _fdset;
			int cnt = select(max_fd+1,&fdset,NULL,NULL,&tv);
			if (cnt <0)
			{
				//exit thread;
				_exit = true;
#ifndef PLATFORM_WIN32
	pthread_exit("net thread exit ...");		
#endif
			}
			else if (cnt >0)
			{
				for (int i = 0; i<= max_fd; i++ )
				{

					 if (FD_ISSET(i,&fdset))
					{
						if (ismgrsock(i))
						{
							//do accept
							struct sockaddr_in clientaddr;
							int size =sizeof(sockaddr_in);
							SY_SOCKET fd = accept(_fd,(struct sockaddr *)&clientaddr,&size);
							int err = get_last_error();
							max_fd = _fd > fd ? _fd : fd;
							//add to cache 			
							setfdnonblock(i);
							_router->add_to_fd_cache(fd,RT_CLIENT);
							FD_SET(fd,&_fdset);
						}
						else
						{
							netcache* cache = _router->get_cache_from_fd(i,RT_CLIENT);
							if(!cache)
								continue;
							unsigned int len = cache->getleftlen() > MAX_TMP_BUFFLEN ? MAX_TMP_BUFFLEN : cache->getleftlen();
							static unsigned char buff[MAX_TMP_BUFFLEN];
							int ret = recv(cache->_fd,(char*)buff,len,0);
							if (ret <=0)
							{
								if (get_last_error() == EAGAIN || get_last_error() == EWOULDBLOCK)
								{
									continue;
								}
								FD_CLR(i, &_fdset);
								shutdown(i, SD_BOTH);
								if(istransmitsocket(i))
								{
									_router->remove_from_fd_cache(i,cache->getroutertype());
									//re connection 
									connecttransmitsvr(925,"127.0.0.1");
								}
								else
								{
									UID uid = cache->_uid;
									_router->remove_from_fd_cache(i,cache->getroutertype());
								}
								
								//notify logic thread net disconnect 
								continue;
							}
							Event ev;
							ev.set_cmd(++s_count);
							std::string tmp;

							ev.SerializeToString(&tmp);
							unsigned int index = 1;
							unsigned int cmd =1;
							unsigned int serverid = 0;
							putdata(cache,index,cmd,(const unsigned char *)tmp.c_str(),tmp.length(),serverid);
							//to do check data and give logic thread dealwith it ...
							int ready = cache->ready();
							if (ready < 0)
							{
								//remove 
								_router->remove_from_fd_cache(cache->_fd,RT_CLIENT);
								FD_CLR(i, &_fdset);
								close_socket(i);
								//notify logic thread net disconnect 
								continue;
							}
							else if(0 == ready )
							{
								//have data to resolove
								static unsigned char databuff[MAX_CACHE_SIZE_CLIENT];
								unsigned int len = cache->get(databuff,MAX_CACHE_SIZE_CLIENT);
								if(len > 0)
								{
									//add to event thread list
									len+=4;
									_io_hander->push_event_data((const unsigned char*)&len,4);
									_io_hander->push_event_data(databuff,len-4);
								}
							}
						}

					}
				}
			}

		} while (!_exit);
		close_socket(_fd);
		//_exit(-1);
		return 0;
	}
	virtual void exit()
	{
		_status =NS_EXIT;
		_exit = true;

	}
	virtual void stop()
	{
		_status =NS_STOP;
	}
	virtual bool ismgrsock(SY_SOCKET fd)
	{
		return fd == _fd ? true : false;
	}
	bool istransmitsocket(SY_SOCKET fd)
	{
		return _transmitfd == fd ? true : false;
	}

public:
	int initsock()
	{
		struct sockaddr_in hostaddr;  
		memset((void *)&hostaddr, 0, sizeof(hostaddr));
		hostaddr.sin_family = AF_INET;  
		hostaddr.sin_port = htons(_port);
		hostaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
		_fd = socket(AF_INET, SOCK_STREAM, 0);
		if (_fd <=0)
		{
			return get_last_error();
		}
		if (_io_hander->routertype() == RT_TRANSMIT)
		{
			if(bind(_fd, (struct sockaddr *)&hostaddr, sizeof(hostaddr)) < 0)  
			{  
				return get_last_error();
			}
			if(listen(_fd, MAX_LISTEN_NUM) < 0)  
			{  
				return get_last_error();
			}  
		}
		return 0;
	}
	int connecttransmitsvr(short port, const char* addr)
	{	
		//init client
		//cn to transmit
		_transmitfd = socket(PF_INET, SOCK_STREAM, 0);
		struct sockaddr_in hints;
		memset(&hints, 0, sizeof (hints));
		hints.sin_family = AF_INET;
		hints.sin_family = SOCK_STREAM;
		hints.sin_addr.s_addr = inet_addr(addr);
		hints.sin_port = htons(port);
		int ret  =0;
// 		if(!setfdnonblock(_transmitfd))
// 			return -1;//fail
		do
		{
			 ret = connect(_transmitfd,(const sockaddr*) &hints, sizeof(sockaddr_in));
		}while(ret !=0);
		//success !!!!
		return ret;
	}
private:
	io_handler* _io_hander;
	NET_STAT _status;
	bool _exit;
	SY_SOCKET _fd;
	SY_SOCKET _transmitfd;//gw only 
	std::string _ip;
	int _port;
	router * _router;
	fd_set _fdset;

};

class gatewayselect: public netselect
{

public:
	virtual bool ismgrsock(SY_SOCKET fd)
	{
		istransmitsock(fd);
	}

	virtual bool istransmitsock(SY_SOCKET fd)
	{
		return _transmit_fd == fd;
	}
protected:
private:
	SY_SOCKET _transmit_fd;
};
#endif///_SELECT_H_2016_12_02_
